//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.UIDesigner
{
    #region NameSpace
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;

    using System.Workflow.ComponentModel;

    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;

    using Microsoft.Research.DataLayer;
    using Type = System.Type;
    using Activity = System.Workflow.ComponentModel.Activity;
    using Microsoft.Practices.EnterpriseLibrary.Logging;
    using Microsoft.Research.ScientificWorkflow.TridentComposer;
    using System.Resources;
    using Microsoft.Research.ScientificWorkflow.Validators;
    using System.Collections.ObjectModel;
    using Microsoft.Research.eResearch.Common.Eventing;
    #endregion

    public class WorkflowActivityElement : TridentUIActivityElement
    {
        #region Declaration

        private ActivityBody body;
        private ActivityHeader header;
        private SimpleActivityModel activityModel;
        private Collection<MenuItem> activitySpecificMenuItems = new Collection<MenuItem>();

        /// <summary>
        /// Occurs when update is clicked.
        /// </summary>
        public override event EventHandler<ModelEventArgs> UpdateClick;

        #endregion

        public WorkflowActivityElement(SimpleActivityModel model, Dictionary<String, DataTypeModel> activityDataTypes)
            : base(model, activityDataTypes)
        {
            this.DataContext = model;
            this.activityModel = model;
            if (!string.IsNullOrEmpty(model.UniqueId))
            {
                this.Name = model.UniqueId + "Element";
            }
            this.Loaded += new RoutedEventHandler(WorkflowActivityElement_Loaded);

            InitComponent();
        }

        #region Public methods

        /// <summary>
        /// Display the parameter outline.
        /// </summary>
        public override void ShowParameterOutline(bool showInputOutline)
        {
            if (this.body != null)
            {
                if (showInputOutline)
                {
                    body.EnableParameterOutline(true);
                }
                else
                {
                    body.EnableParameterOutline(false);
                }
            }
        }

        /// <summary>
        /// Hide the parameter outline.
        /// </summary>
        public override void HideParameterOutline(bool hideInputOutline)
        {
            if (this.body != null)
            {
                if (hideInputOutline)
                {
                    body.DisableParameterOutline(true);
                }
                else
                {
                    body.DisableParameterOutline(false);
                }
            }
        }

        /// <summary>
        /// Extend the context menu to have specific item for this.
        /// </summary>
        public override void CreateContextMenu()
        {
            base.CreateContextMenu();
            if (this.Model.IsSimpleActivityModel && this.ContextMenu != null)
            {
                this.ContextMenu.Items.Add(this.ActivityShowHideContextMenu());
                this.ContextMenu.Opened += new RoutedEventHandler(this.OnContextMenuOpened);
            }

            this.ValidateAndAddCopyContextMenu();
        }

        private void OnContextMenuOpened(object sender, RoutedEventArgs e)
        {
            if (this.ComposerCtrl != null)
            {
                bool singleSelection = this.ComposerCtrl.CurrentSelectedElements.Count > 1 ? false : true;
                MenuItem copyItem = null;
                foreach (MenuItem item in this.activitySpecificMenuItems)
                {
                    item.Visibility = singleSelection ? Visibility.Visible : Visibility.Collapsed;
                }

                foreach (MenuItem item in this.ContextMenu.Items)
                {
                    if (item.Tag != null && item.Tag.ToString().Equals("copyMenu", StringComparison.OrdinalIgnoreCase))
                    {
                        copyItem = item;
                    }
                }

                if (copyItem != null)
                {
                    copyItem.Visibility = singleSelection ? Visibility.Visible : Visibility.Collapsed;
                }
            }
        }

        /// <summary>
        /// Add copy context menu only for simple activities
        /// </summary>
        private void ValidateAndAddCopyContextMenu()
        {
            // not require to add copy menu for Composite activity
            if (!this.activityModel.IsCompositeActivityModel)
            {
                MenuItem copyMenu = new MenuItem();
                copyMenu.Tag = "copyMenu";
                copyMenu.Header = TridentResourceManager.GetString("CopyMenuLabel");
                copyMenu.Icon = TridentUIActivityElement.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ScientificWorkflow/copyActivity.png");
                copyMenu.Click += new RoutedEventHandler(this.OnActivityCopy);
                this.ContextMenu.Items.Add(copyMenu);
            }
        }

        /// <summary>
        /// Fired on click of Copy Activity.
        /// </summary>
        /// <param name="sender">
        /// Context menu.
        /// </param>
        /// <param name="e">
        /// RoutedEventArgs.
        /// </param>
        private void OnActivityCopy(object sender, RoutedEventArgs e)
        {
            try
            {
                TridentClipboardService.Clear();
                TridentClipboardService.Data = this.activityModel.CopyActivityModel();
            }
            catch (TridentCustomException exception)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(exception.CustomMessage.ToString());
            }
        }

        /// <summary>
        /// Event handler for the loaded event of the activity element.
        /// </summary>
        /// <param name="sender">The origin of the event.</param>
        /// <param name="e">Event arguments.</param>
        private void WorkflowActivityElement_Loaded(object sender, RoutedEventArgs e)
        {
            // Collapse the body if the flag is set to true.
            if (this.activityModel.IsCollapsed)
            {
                this.CollapseBody();
            }
        }

        /// <summary>
        /// Override to support different handling for the monitor mode.
        /// </summary>
        protected override void AddInteractionHandlers()
        {
            if (this.ComposerCtrl != null)
            {
                if (this.ComposerCtrl.CurrentRenderMode == RenderMode.ComposerMode ||
                    this.ComposerCtrl.CurrentRenderMode == RenderMode.ProvenanceMode)
                {
                    base.AddInteractionHandlers();
                }
                else if (this.ComposerCtrl.CurrentRenderMode == RenderMode.MonitorMode ||
                    this.ComposerCtrl.CurrentRenderMode == RenderMode.VisualizationMode)
                {
                    this.ContextMenu = null;

                    // Collapse/Show Menu is not required for Composite activity
                    if (this.header != null && this.body != null && this.activityModel != null && this.activityModel.IsSimpleActivityModel)
                    {
                        this.body.ContextMenu = new ContextMenu();
                        this.header.ContextMenu = new ContextMenu();
                        this.header.ContextMenu.Items.Add(this.ActivityShowHideContextMenu());
                        this.body.ContextMenu.Items.Add(this.ActivityShowHideContextMenu());
                    }
                }
            }
        }

        /// <summary>
        /// Collapse hide Activity menu
        /// </summary>
        /// <returns>MenuItem</returns>
        private MenuItem ActivityShowHideContextMenu()
        {
            MenuItem activityCollapsedMenu = new MenuItem();
            if (this.Model.IsCollapsed)
            {
                activityCollapsedMenu.Header = TridentResourceManager.GetString("ShowActivity");
                activityCollapsedMenu.Icon = TridentUIActivityElement.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ScientificWorkflow/ShowActivities.png");
            }
            else
            {
                activityCollapsedMenu.Header = TridentResourceManager.GetString("CollapseActivity");
                activityCollapsedMenu.Icon = TridentUIActivityElement.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ScientificWorkflow/HideActivities.png");
            }

            activityCollapsedMenu.Click += new RoutedEventHandler(this.OnSimpleActivityMenuClick);
            activityCollapsedMenu.Loaded += new RoutedEventHandler(this.OnSimpleActivityMenuLoaded);
            this.activitySpecificMenuItems.Add(activityCollapsedMenu);
            return activityCollapsedMenu;
        }

        /// <summary>
        /// Event handler for the Context menu item loaded event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnSimpleActivityMenuLoaded(object sender, RoutedEventArgs e)
        {
            MenuItem activityCollapsedMenu = sender as MenuItem;
            if (null != activityCollapsedMenu)
            {
                if (this.Model.IsCollapsed)
                {
                    activityCollapsedMenu.Header = TridentResourceManager.GetString("ShowActivity");
                    activityCollapsedMenu.Icon = TridentUIActivityElement.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ScientificWorkflow/ShowActivities.png");

                }
                else
                {
                    activityCollapsedMenu.Header = TridentResourceManager.GetString("CollapseActivity");
                    activityCollapsedMenu.Icon = TridentUIActivityElement.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ScientificWorkflow/HideActivities.png");
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnSimpleActivityMenuClick(object sender, RoutedEventArgs e)
        {
            MenuItem activityCollapsedMenu = sender as MenuItem;
            if (null != activityCollapsedMenu)
            {
                if (activityCollapsedMenu.Header.ToString().Equals(TridentResourceManager.GetString("ShowActivity")))
                {
                    this.ExpandBody();
                    activityCollapsedMenu.Header = TridentResourceManager.GetString("CollapseActivity");
                    activityCollapsedMenu.Icon = TridentUIActivityElement.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ScientificWorkflow/HideActivities.png");
                }
                else
                {
                    this.CollapseBody();
                    activityCollapsedMenu.Header = TridentResourceManager.GetString("ShowActivity");
                    activityCollapsedMenu.Icon = TridentUIActivityElement.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ScientificWorkflow/ShowActivities.png");
                }

                if (this.ComposerCtrl != null)
                {
                    this.ComposerCtrl.CurrentComposerRenderHelper.parameterBindingVisitor.RefreshBindings();
                }
            }
        }

        #endregion

        /// <summary>
        /// Initialize the Activity component.
        /// </summary>
        private void InitComponent()
        {
            SimpleActivityModel activity = DataContext as SimpleActivityModel;

            this.header = this.InitHeader(activity);
            DockPanel.SetDock(header, Dock.Top);

            this.body = this.InitBody(activity);

            Grid childGrid = new Grid();
            childGrid.Children.Add(body);
            childGrid.Children.Add(header);

            this.MainBorder = new Border();
            this.MainBorder.Child = childGrid;
            this.MainBorder.BorderThickness = new Thickness(1.5);
            this.MainBorder.Padding = new Thickness(10, 10, 10, 0);

            this.contentHolder.Children.Add(MainBorder);

            this.Margin = new Thickness(30, 5, 30, 5);
        }

        /// <summary>
        /// Initialize the header of the activity element.
        /// </summary>
        /// <param name="model">The activity model.</param>
        /// <returns>The control created.</returns>
        private ActivityHeader InitHeader(SimpleActivityModel model)
        {
            ActivityHeader activityHeader = new ActivityHeader(model);
            this.LeftControlFlowConnectionElement = activityHeader.rectangleLeft;
            this.RightControlFlowConnectionElement = activityHeader.rectangleRight;
            activityHeader.HeaderText = model.Label;
            activityHeader.OnOptionsClick += new EventHandler(this.OnActivityOptionsClick);
            activityHeader.UpdateClick += delegate(object sender, ModelEventArgs args) { this.UpdateClick.Fire(sender, args); };

            if (model.IsBuiltIn)
            {
                GradientStop gs1 = new GradientStop((Color)ColorConverter.ConvertFromString("#FFEEFADE"), 0);
                GradientStop gs2 = new GradientStop((Color)ColorConverter.ConvertFromString("#FFD3E8AB"), 1);
                GradientStopCollection gsColl = new GradientStopCollection(2);
                gsColl.Add(gs1);
                gsColl.Add(gs2);

                LinearGradientBrush lnrGradBrush = new LinearGradientBrush(gsColl, new Point(0.5, 0), new Point(0.5, 1));
                activityHeader.HeaderBackground = lnrGradBrush;
            }

            return activityHeader;
        }

        /// <summary>
        /// Initialize the body of the activity.
        /// </summary>
        /// <param name="activity">The activity model.</param>
        /// <returns>The control created.</returns>
        private ActivityBody InitBody(SimpleActivityModel model)
        {
            ActivityBody activityBody = new ActivityBody(model, this.InputParameterElements, this.OutputParameterElements);
            if (model.IsBuiltIn)
            {
                GradientStop gs1 = new GradientStop((Color)ColorConverter.ConvertFromString("#FFFBFFF3"), 0);
                GradientStop gs2 = new GradientStop((Color)ColorConverter.ConvertFromString("#FFE8F6D2"), 1);
                GradientStopCollection gsColl = new GradientStopCollection(2);
                gsColl.Add(gs1);
                gsColl.Add(gs2);
                LinearGradientBrush lnrGradBrush = new LinearGradientBrush(gsColl, new Point(0.5, 0), new Point(0.5, 1));
                activityBody.BodyBackground = lnrGradBrush;
            }
            else
            {
                GradientStop gs1 = new GradientStop((Color)ColorConverter.ConvertFromString("#FFFFFFFF"), 0);
                GradientStop gs2 = new GradientStop((Color)ColorConverter.ConvertFromString("#FFECF0F1"), 1);
                GradientStopCollection gsColl = new GradientStopCollection(2);
                gsColl.Add(gs1);
                gsColl.Add(gs2);
                LinearGradientBrush lnrGradBrush = new LinearGradientBrush(gsColl, new Point(0.5, 0), new Point(0.5, 1));
                activityBody.BodyBackground = lnrGradBrush;
            }

            return activityBody;
        }

        /// <summary>
        /// Fired on click of the options button.
        /// </summary>
        /// <param name="sender">
        /// Options button.
        /// </param>
        /// <param name="e">
        /// EventArgs.
        /// </param>
        private void OnActivityOptionsClick(object sender, EventArgs e)
        {
            if (this.ComposerCtrl != null)
            {
                this.ComposerCtrl.CurrentSelectedElements.RemoveAllItems();
                this.ComposerCtrl.CurrentSelectedElements.SelectChildrenUpTo(this);
            }

            if (this.ContextMenu != null)
            {
                ////Set the placement target to Application main window, Test Automation requires the context menu
                ////to be part of the Trident application. Currently it appears outside the Trident application
                if (!(this.ContextMenu.PlacementTarget is Window))
                {
                    this.ContextMenu.PlacementTarget = Application.Current.MainWindow;
                }
                this.ContextMenu.IsOpen = true;
            }
            if (this.ComposerCtrl != null &&
                (this.ComposerCtrl.CurrentRenderMode == RenderMode.MonitorMode ||
                this.ComposerCtrl.CurrentRenderMode == RenderMode.VisualizationMode))
            {
                if (this.body.ContextMenu != null)
                {
                    this.body.ContextMenu.IsOpen = true;
                }
            }

        }

        /// <summary>
        /// Toggles the parameter bindings.
        /// </summary>
        /// <param name="sender">
        /// Parameter element.
        /// </param>
        /// <param name="e">
        /// EventArgs.
        /// </param>
        private void OnToggleParameterBindings(object sender, EventArgs e)
        {
            if (this.activityModel.CurrentParameterBindingVisibility == Visibility.Collapsed)
            {
                base.ToggleParameterBindings(Visibility.Visible);
            }
            else
            {
                base.ToggleParameterBindings(Visibility.Collapsed);
            }
        }

        /// <summary>
        /// Updates the execution icon for the element
        /// </summary>
        /// <param name="imageSource"></param>
        private void UpdateExecutionIcon(string imageSource)
        {
            DockPanel body = ((DockPanel)((DockPanel)this.contentHolder.Children[1]).Children[0]);
            body.Children.Clear();
            Canvas iconPanel = new Canvas();
            Image icon = new Image();

            ImageSourceConverter converter = new ImageSourceConverter();
            icon.Source = (ImageSource)converter.ConvertFromString(imageSource);
            icon.Height = 30;

            iconPanel.Height = 0;

            body.Children.Add(iconPanel);
            iconPanel.Children.Add(icon);

            Canvas.SetBottom(icon, -15);
            Canvas.SetLeft(icon, -15);

            DockPanel.SetDock(iconPanel, Dock.Bottom);
        }

        /// <summary>
        /// Collapse the body.
        /// </summary>
        private void CollapseBody()
        {
            if (this.body != null)
            {
                this.body.Visibility = Visibility.Collapsed;
                this.activityModel.IsCollapsed = true;
                if (this.ComposerCtrl != null
                    && this.ComposerCtrl.CurrentComposerRenderHelper != null
                    && this.ComposerCtrl.CurrentComposerRenderHelper.connectionVisitor != null)
                {
                    this.ComposerCtrl.CurrentComposerRenderHelper.connectionVisitor.InsertActivityConnection();
                }
            }
        }

        /// <summary>
        /// Maximize the body.
        /// </summary>
        private void ExpandBody()
        {
            if (this.body != null)
            {
                this.body.Visibility = Visibility.Visible;
                this.activityModel.IsCollapsed = false;
                if (this.ComposerCtrl != null
                    && this.ComposerCtrl.CurrentComposerRenderHelper != null
                    && this.ComposerCtrl.CurrentComposerRenderHelper.connectionVisitor != null)
                {
                    this.ComposerCtrl.CurrentComposerRenderHelper.connectionVisitor.InsertActivityConnection();
                }
            }
        }

        public override void ApplySelectedStyle()
        {
            // Apply the background of the border.
            this.MainBorder.BorderBrush = Brushes.Orange;

            this.MainBorder.CornerRadius = new CornerRadius(5);
            this.MainBorder.VerticalAlignment = VerticalAlignment.Top;
        }

        public override void ApplyUnselectedStyle()
        {
            // Revert back all the changes made while selecting.
            this.MainBorder.BorderBrush = new SolidColorBrush(Colors.Transparent);

            this.MainBorder.CornerRadius = new CornerRadius(0);
            this.MainBorder.VerticalAlignment = VerticalAlignment.Top;
        }

        /// <summary>
        /// This function is used to update the Icon of the Parameter Binding image in 
        /// workflow activity element based on the visibilty of the parameter bindings.
        /// </summary>
        /// <param name="changeVisibilty">Visibilty of the cativity model.</param>
        public void UpdateParameterBindingDetail(Visibility changeVisibility)
        {
            if (null != this.header)
            {
                MenuItem hideParamBindingsMenu = this.GetParameterMenuItem();
                if (hideParamBindingsMenu != null)
                {
                    if (Visibility.Visible == changeVisibility)
                    {
                        hideParamBindingsMenu.Header = TridentResourceManager.GetString("HideParameterBindingText");
                        hideParamBindingsMenu.Icon = GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ScientificWorkflow/HideParamBindings.png");
                    }
                    else
                    {
                        hideParamBindingsMenu.Header = TridentResourceManager.GetString("ShowParameterBindingText");
                        hideParamBindingsMenu.Icon = GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ScientificWorkflow/ShowParamBindings.png");
                    }
                }
            }
        }

        /// <summary>
        ///Retrives the  ParameterBinding Menu. from  the context menu
        /// </summary>
        /// <returns>MenuItem</returns>
        private MenuItem GetParameterMenuItem()
        {
            if (this.ContextMenu != null)
            {
                foreach (MenuItem menu in this.ContextMenu.Items)
                {
                    if (TridentResourceManager.GetString("HideParameterBindingText").Equals(menu.Header.ToString())
                        || TridentResourceManager.GetString("ShowParameterBindingText").Equals(menu.Header.ToString()))
                    {
                        return menu;
                    }

                }
            }
            return null;
        }

        public override Point GetBindingStartPoint(UIElement relativeTo)
        {
            if (this.activityModel.IsCollapsed)
            {
                // Get the start point in the activity header.
                Point startPoint = new Point(this.header.ActualWidth + 10, this.header.ActualHeight / 2.0 + 10.0);
                return this.TranslatePoint(startPoint, relativeTo);
            }
            else
            {
                // Get the midpoint of the outline.
                ActivityBody body = this.body as ActivityBody;
                if (body != null)
                {
                    return body.GetOutputBorderConnectionPosition(relativeTo);
                }
                else
                {
                    return new Point();
                }
            }
        }

        public override Point GetBindingEndpoint(UIElement relativeTo)
        {
            if (this.activityModel.IsCollapsed)
            {
                // Get the start point in the activity header.
                Point startPoint = new Point(10, this.header.ActualHeight / 2.0 + 10.0);
                return this.TranslatePoint(startPoint, relativeTo);
            }
            else
            {
                // Get the midpoint of the outline.
                ActivityBody body = this.body as ActivityBody;
                if (body != null)
                {
                    return body.GetInputBorderConnectionPosition(relativeTo);
                }
                else
                {
                    return new Point();
                }
            }
        }
    }
}

